package client

import (
	"time"

	"google.golang.org/protobuf/proto"

	"gitee.com/hasika/common-server/codec"
	"gitee.com/hasika/common-server/conn/inet"
	"gitee.com/hasika/common-server/option"
)

type DispatchMessageFunc func(data codec.RspPacket, conn inet.Conn)

type MessageDispatcher interface {
	DispatchMessage(data codec.RspPacket, conn inet.Conn)
}

type RawClientHandler[T Req, H Rsp] func(client *Client[T, H])
type PreProcessor[T Req] func(req T)
type PostProcessor[H Rsp] func(rsp H, err error) (proto.Message, error)

type Option[T Req, H Rsp] struct {
	reqCoder           codec.ICoder
	rspCoder           codec.IRspCoder
	connectionCallback *inet.BaseConnectionCallback
	dispatcher         MessageDispatcher
	dials              []*option.RemoteOption
	heartBeatFunc      func(client *Client[T, H])
	heartBeatDuration  time.Duration
	bindFunc           func(client *Client[T, H])
	buildReqFunc       func(message proto.Message) T
	postProcessor      PostProcessor[H]
	kcpOption          *option.KcpOption
	rcvQueueSize       int
	sendQueueSize      int
}

func NewOption[T Req, H Rsp]() *Option[T, H] {
	return &Option[T, H]{
		connectionCallback: &inet.BaseConnectionCallback{},
		rcvQueueSize:       1024,
		sendQueueSize:      1024,
	}
}

func (c *Option[T, H]) SetReqCoder(reqCoder codec.ICoder) *Option[T, H] {
	c.reqCoder = reqCoder
	return c
}

func (c *Option[T, H]) SetRspCoder(rspCoder codec.IRspCoder) *Option[T, H] {
	c.rspCoder = rspCoder
	return c
}

func (c *Option[T, H]) AddConnectionCallback(connectionCallback inet.ConnectionCallback) *Option[T, H] {
	c.connectionCallback.AddCallback(connectionCallback)
	return c
}

func (c *Option[T, H]) AddDialOptions(op *option.RemoteOption) *Option[T, H] {
	c.dials = append(c.dials, op)
	return c
}

func (c *Option[T, H]) SetDispatcher(dispatcher MessageDispatcher) *Option[T, H] {
	c.dispatcher = dispatcher
	return c

}

func (c *Option[T, H]) SetHeartBeatFunc(heartBeatFunc RawClientHandler[T, H]) *Option[T, H] {
	c.heartBeatFunc = heartBeatFunc
	return c
}

func (c *Option[T, H]) SetBindFunc(bindFunc RawClientHandler[T, H]) *Option[T, H] {
	c.bindFunc = bindFunc
	return c
}

func (c *Option[T, H]) SetBuildReqFunc(buildReqFunc func(message proto.Message) T) *Option[T, H] {
	c.buildReqFunc = buildReqFunc
	return c
}

func (c *Option[T, H]) GetReqCoder() codec.ICoder {
	return c.reqCoder
}

func (c *Option[T, H]) GetRspCoder() codec.IRspCoder {
	return c.rspCoder
}

func (c *Option[T, H]) GetConnectionCallback() inet.ConnectionCallback {
	return c.connectionCallback
}

func (c *Option[T, H]) GetMessageDispatcher() MessageDispatcher {
	return c.dispatcher
}

func (c *Option[T, H]) GetDialOptions() []*option.RemoteOption {
	return c.dials
}

func (c *Option[T, H]) GetHeartBeatFunc() RawClientHandler[T, H] {
	return c.heartBeatFunc
}

func (c *Option[T, H]) GetBindFunc() RawClientHandler[T, H] {
	return c.bindFunc
}

func (c *Option[T, H]) GetBuildReqFunc() func(message proto.Message) T {
	return c.buildReqFunc
}

func (c *Option[T, H]) GetRspPostProcessor() PostProcessor[H] {
	return c.postProcessor
}

func (c *Option[T, H]) SetRspPostProcessor(r PostProcessor[H]) *Option[T, H] {
	c.postProcessor = r
	return c
}

func (c *Option[T, H]) KcpOption() *option.KcpOption {
	return c.kcpOption
}

func (c *Option[T, H]) SetKcpOption(kcpOption *option.KcpOption) *Option[T, H] {
	c.kcpOption = kcpOption
	return c
}

func (c *Option[T, H]) HeartBeatDuration() time.Duration {
	if c.heartBeatDuration == 0 {
		return time.Second * 5
	}
	return c.heartBeatDuration
}

func (c *Option[T, H]) SetHeartBeatDuration(heartBeatDuration time.Duration) *Option[T, H] {
	c.heartBeatDuration = heartBeatDuration
	return c
}

func (c *Option[T, H]) SetRcvQueueSize(rcvQueueSize int) *Option[T, H] {
	c.rcvQueueSize = rcvQueueSize
	return c
}

func (c *Option[T, H]) SetSendQueueSize(sendQueueSize int) *Option[T, H] {
	c.sendQueueSize = sendQueueSize
	return c
}

func (c *Option[T, H]) RcvQueueSize() int {
	return c.rcvQueueSize
}

func (c *Option[T, H]) SendQueueSize() int {
	return c.sendQueueSize
}
